package com.gjiazhe.wavesidebar;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.util.Arrays;

/**
 * Created by gjz on 8/23/16.
 */
public class WaveSideBar extends Component implements Component.DrawTask, Component.TouchEventListener{
    private boolean isInclude;
    private boolean isFirst = true;
    private Color mTextColor = Color.BLACK;

    //文字大小
    private final static int DEFAULT_TEXT_SIZE = 14; // fp

    //控件偏移宽度
    private final static int DEFAULT_MAX_OFFSET = 100; //vp

    private final static String[] DEFAULT_INDEX_ITEMS = {"A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

    private String[] mIndexItems;

    /**
     *当前选中的索引项mIndexItems中的索引。
     *当手指向上时，它重置为-1
     */
    private int mCurrentIndex = -1;

    /**
     *手指触摸点Y坐标。
     *基线在mStartTouchingArea的顶部
     *当手指向上时，它重置为-1
     */
    private float mCurrentY = 5;

    private Paint mPaint;
    private float mTextSize;

    /**
     *每项索引的高度
     */
    private float mIndexItemHeight;

    /**
     *当前选中索引项的偏移量
     */
    private float mMaxOffset;

    private Rect mStartTouchingArea = new Rect();

    /**
     * mStartTouchingArea的高度和宽度
     */
    private float mBarHeight;
    private float mBarWidth;


    private boolean mStartTouching = false;

    /**
     *如果为true，则{@link OnSelectIndexItemListener#onSelectIndexItem(String)}
     *将不会被调用，直到手指向上。
     *假的，当手指向下、向上和移动时，会叫它。
     */
    private boolean mLazyRespond = false;

    /**
     *侧栏位置，默认为POSITION_RIGHT
     *左手手机用户可设置为POSITION_LEFT
     */
    private int mSideBarPosition;
    public static final int POSITION_RIGHT = 0;
    public static final int POSITION_LEFT = 1;

    /**
     * 项的对齐方式，默认为TEXT_ALIGN_CENTER
     */
    private int mTextAlignment;
    public static final int TEXT_ALIGN_CENTER = 0;
    public static final int TEXT_ALIGN_LEFT = 1;
    public static final int TEXT_ALIGN_RIGHT = 2;

    /**
     * 观察当前选定的索引项
     */
    private OnSelectIndexItemListener onSelectIndexItemListener;

    /**
     *要绘制的第一个索引项文本的基线
     */
    private float mFirstItemBaseLineY;
    private Context mContext;

    public WaveSideBar(Context context) {
        this(context,null);
    }

    public WaveSideBar(Context context, AttrSet attrs) {
        this(context, attrs,"");
    }

    public WaveSideBar(Context context, AttrSet attrs, String styleName) {
        super(context, attrs, styleName);
        mContext = context;
        if(attrs != null){
            mLazyRespond = attrs.getAttr(AttrSetString.GRAPH_VIEW_LAZY_RESPOND).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_LAZY_RESPOND).get().getBoolValue() : false;
            mTextColor = attrs.getAttr(AttrSetString.GRAPH_VIEW_SERIES_COLOR).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_SERIES_COLOR).get().getColorValue() : mTextColor;
            mTextSize = attrs.getAttr(AttrSetString.GRAPH_VIEW_SERIES_SIZE).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_SERIES_SIZE).get().getDimensionValue() : fp2px(context,DEFAULT_TEXT_SIZE);
            mMaxOffset = attrs.getAttr(AttrSetString.GRAPH_VIEW_MAX_OFFSET).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_MAX_OFFSET).get().getDimensionValue() : vp2px(context,DEFAULT_MAX_OFFSET);
            mSideBarPosition =  attrs.getAttr(AttrSetString.GRAPH_VIEW_POSITION).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_POSITION).get().getIntegerValue() : POSITION_RIGHT;
            mTextAlignment = attrs.getAttr(AttrSetString.GRAPH_VIEW_TEXT_ALIGNMENT).isPresent() ? attrs.getAttr(AttrSetString.GRAPH_VIEW_TEXT_ALIGNMENT).get().getIntegerValue() : TEXT_ALIGN_CENTER;
        }else {
            mTextSize =  fp2px(context,DEFAULT_TEXT_SIZE);
            mMaxOffset =  vp2px(context,DEFAULT_MAX_OFFSET);
            mSideBarPosition =  POSITION_RIGHT;
            mTextAlignment =  TEXT_ALIGN_CENTER;
        }

        mIndexItems = DEFAULT_INDEX_ITEMS;
        initPaint();
        //添加绘制任务
        addDrawTask(this::onDraw);
        //设置TouchEvent监听
        setTouchEventListener(this::onTouchEvent);
    }

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(mTextColor);
        mPaint.setTextSize((int) mTextSize);
        switch (mTextAlignment) {
            //设置画笔位置
            case TEXT_ALIGN_CENTER: mPaint.setTextAlign(TEXT_ALIGN_CENTER); break;
            case TEXT_ALIGN_LEFT:   mPaint.setTextAlign(TEXT_ALIGN_LEFT); break;
            case TEXT_ALIGN_RIGHT:  mPaint.setTextAlign(TEXT_ALIGN_RIGHT); break;
        }
    }

    //测量
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);

        Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        mIndexItemHeight = fontMetrics.bottom - fontMetrics.top;//画笔的高度
        mBarHeight = mIndexItems.length * mIndexItemHeight;//文字的总高度

        // 计算最长文本的宽度作为侧栏的宽度
        for (String indexItem : mIndexItems) {
            mBarWidth = Math.max(mBarWidth, mPaint.measureText(indexItem));
        }

        //要绘制的第一项文本的基线Y
        mFirstItemBaseLineY = mIndexItemHeight + getPaddingTop();

        float areaLeft = (mSideBarPosition == POSITION_LEFT) ? 0 : (width - mBarWidth - getPaddingRight());
        float areaRight = (mSideBarPosition == POSITION_LEFT) ? (getPaddingLeft() + areaLeft + mBarWidth) : width;
        float areaTop = height/2 - mBarHeight/2;
        float areaBottom = areaTop + mBarHeight;

        mStartTouchingArea.set(
                (int)areaLeft,
                (int)areaTop,
                (int)areaRight,
                (int)areaBottom);

        // the baseline Y of the first item' text to draw
        mFirstItemBaseLineY = (height/2 - mIndexItems.length*mIndexItemHeight/2)
                + (mIndexItemHeight/2 - (fontMetrics.descent-fontMetrics.ascent)/2)
                - fontMetrics.ascent;
        isFirst = false;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if(isFirst){
            onMeasure(component.getWidth(),component.getHeight());
        }
        // 绘制每个项目
        for (int i = 0, mIndexItemsLength = mIndexItems.length; i < mIndexItemsLength; i++) {
            float baseLineY = mFirstItemBaseLineY + mIndexItemHeight*i;

            // 计算要绘制项目的比例系数
            float scale = getItemScale(i);
            float alphaScale = (i == mCurrentIndex) ? 1 : 1-scale;
            //即透明度。其取值范围是0---255,数值越小，越透明，颜色上表现越淡。
            mPaint.setAlpha(alphaScale);
            mPaint.setTextSize((int) (mTextSize + mTextSize*scale));
            float baseLineX = 0f;
            if (mSideBarPosition == POSITION_LEFT) {
                switch (mTextAlignment) {
                    case TEXT_ALIGN_CENTER:
                        baseLineX = getPaddingLeft() + mBarWidth/2 - mPaint.measureText(mIndexItems[i])/2 + mMaxOffset*scale;
                        break;
                    case TEXT_ALIGN_LEFT:
                        baseLineX = getPaddingLeft() + mMaxOffset*scale;
                        break;
                    case TEXT_ALIGN_RIGHT:
                        baseLineX = getPaddingLeft() + mBarWidth - mPaint.measureText(mIndexItems[i]) + mMaxOffset*scale;
                        break;
                }
            } else {
                switch (mTextAlignment) {
                    case TEXT_ALIGN_CENTER:
                        baseLineX = getWidth() - getPaddingRight() - mBarWidth/2 - mPaint.measureText(mIndexItems[i])/2 - mMaxOffset*scale;
                        break;
                    case TEXT_ALIGN_RIGHT:
                        baseLineX = getWidth() - getPaddingRight() - mPaint.measureText(mIndexItems[i]) - mMaxOffset*scale;
                        break;
                    case TEXT_ALIGN_LEFT:
                        baseLineX = getWidth() - getPaddingRight() - mBarWidth - mMaxOffset*scale;
                        break;
                }
            }
            // draw
            canvas.drawText(
                    mPaint,
                    mIndexItems[i],
                    baseLineX,
                    baseLineY);
        }

        // reset paint
        mPaint.setAlpha(1);
        mPaint.setTextSize((int) mTextSize);
    }

    /**
     *计算要绘制项目的比例系数,控制x偏移,文字大小
     *
     * @param index index项在数组mIndexItems中的索引
     * @return 要绘制的项的缩放因子
     */
    private float getItemScale(int index) {
        float scale = 0;
        if (mCurrentIndex != -1) {
            float distance = Math.abs(mCurrentY - (mIndexItemHeight*index+mIndexItemHeight/2)) / mIndexItemHeight;
            scale = 1 - distance*distance/16;
            scale = Math.max(scale, 0);
        }
        return scale;
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (mIndexItems.length == 0) {
            return false;
        }
        MmiPoint point = touchEvent.getPointerPosition(touchEvent.getIndex());
        float eventY = point.getY();
        float eventX = point.getX();
        mCurrentIndex = getSelectedIndex(eventY);

        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                //判断是否包含某点
                if (mStartTouchingArea.isInclude((int)eventX, (int)eventY)) {
                    mStartTouching = true;
                    if (!mLazyRespond && onSelectIndexItemListener != null) {
                        onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);
                    }
                    invalidate();
                    isInclude = true;
                    return true;
                } else {
                    mCurrentIndex = -1;
                    isInclude = false;
                    return false;
                }
            case TouchEvent.POINT_MOVE:
                if (isInclude){
                    if (mStartTouching && !mLazyRespond && onSelectIndexItemListener != null) {
                        onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);
                    }
                    invalidate();
                }
                return true;
            case TouchEvent.PRIMARY_POINT_UP:
                if (isInclude){
                    if (mLazyRespond && onSelectIndexItemListener != null) {
                        onSelectIndexItemListener.onSelectIndexItem(mIndexItems[mCurrentIndex]);
                    }
                    mCurrentIndex = -1;
                    mStartTouching = false;
                    invalidate();
                }
                return true;
        }
        return false;
    }

    private int getSelectedIndex(float eventY) {
        mCurrentY = eventY - (getHeight()/2 - mBarHeight /2);
        if (mCurrentY <= 0) {
            return 0;
        }

        int index = (int) (mCurrentY / this.mIndexItemHeight);
        if (index >= this.mIndexItems.length) {
            index = this.mIndexItems.length - 1;
        }
        return index;
    }

    private float vp2px(Context context,float vp) {
        return  AttrHelper.vp2px(vp,context);
    }

    private float fp2px(Context context,float fp) {
        return  AttrHelper.fp2px(fp,context);
    }

    /**
     *自定义侧边栏的文字内容
     *
     * @param indexItems 文字
     */
    public void setIndexItems(String... indexItems) {
        mIndexItems = Arrays.copyOf(indexItems, indexItems.length);
        invalidate();
    }

    /**
     *设置侧边栏文字颜色
     *
     * @param color 文字颜色
     */
    public void setTextColor(Color color) {
        mTextColor = color;
        mPaint.setColor(color);
        invalidate();
    }

    /**
     *侧栏位置
     *
     * @param position 侧栏位置
     */
    public void setPosition(int position) {
        if (position != POSITION_RIGHT && position != POSITION_LEFT) {
            throw new IllegalArgumentException("the position must be POSITION_RIGHT or POSITION_LEFT");
        }

        mSideBarPosition = position;
        invalidate();
    }

    /**
     *当前选中索引项的偏移量
     *
     * @param offset 偏移量
     */
    public void setMaxOffset(int offset) {
        mMaxOffset = offset;
        invalidate();
    }

    /**
     *如果为true，则直到手指抬起时，才会调用侦听器。如果false调用时，手指向下，移动和向上
     *
     * @param lazyRespond 是否懒加载
     */
    public void setLazyRespond(boolean lazyRespond) {
        mLazyRespond = lazyRespond;
    }

    /**
     *项的对齐方式
     *
     * @param align 项的对齐方式
     */
    public void setTextAlign(int align) {
        if (mTextAlignment == align) {
            return;
        }
        switch (align) {
            case TEXT_ALIGN_CENTER: mPaint.setTextAlign(TEXT_ALIGN_CENTER); break;
            case TEXT_ALIGN_LEFT:   mPaint.setTextAlign(TEXT_ALIGN_LEFT); break;
            case TEXT_ALIGN_RIGHT:  mPaint.setTextAlign(TEXT_ALIGN_RIGHT); break;
            default:
                throw new IllegalArgumentException(
                        "the alignment must be TEXT_ALIGN_CENTER, TEXT_ALIGN_LEFT or TEXT_ALIGN_RIGHT");
        }
        mTextAlignment = align;
        invalidate();
    }

    /**
     *设置侧边栏文字大小
     *
     * @param size 文字大小
     */
    public void setTextSize(float size) {
        float textSize = fp2px(mContext,size);
        if (mTextSize == textSize) {
            return;
        }
        mTextSize = textSize;
        mPaint.setTextSize((int) textSize);
        invalidate();
    }

    public void setOnSelectIndexItemListener(OnSelectIndexItemListener onSelectIndexItemListener) {
        this.onSelectIndexItemListener = onSelectIndexItemListener;
    }

    public interface OnSelectIndexItemListener {
        void onSelectIndexItem(String index);
    }
}

